﻿%define pg.RootNamespace ParserGenerator
%define pg.SyntacticAnalysisNamespace ParserGenerator.Languages.C
%define pg.SourceContextNamespace ParserGenerator.Input
%define pg.OutputNamespace ParserGenerator.Output

%define pg.UsePythonLikeIndentation False
%define pg.indented False

%define pg.parser.classname CProgramParser
%define pg.parser.namespace ParserGenerator.Languages.C

%define pg.parser.usings (
    ParserGenerator.Collections,
)

%keyword KeywordAuto ('auto')
%keyword KeywordBreak ('break')
%keyword KeywordCase ('case')
%keyword KeywordChar ('char')
%keyword KeywordConst ('const')
%keyword KeywordContinue ('continue')
%keyword KeywordDefault ('default')
%keyword KeywordDo ('do')
%keyword KeywordDouble ('double')
%keyword KeywordElse ('else')
%keyword KeywordEnum ('enum')
%keyword KeywordExtern ('extern')
%keyword KeywordFloat ('float')
%keyword KeywordFor ('for')
%keyword KeywordGoto ('goto')
%keyword KeywordIf ('if')
%keyword KeywordInline ('inline')
%keyword KeywordInt ('int')
%keyword KeywordLong ('long')
%keyword KeywordRegister ('register')
%keyword KeywordRestrict ('restrict')
%keyword KeywordReturn ('return')
%keyword KeywordShort ('short')
%keyword KeywordSigned ('signed')
%keyword KeywordSizeof ('sizeof')
%keyword KeywordStatic ('static')
%keyword KeywordStruct ('struct')
%keyword KeywordSwitch ('switch')
%keyword KeywordTypedef ('typedef')
%keyword KeywordUnion ('union')
%keyword KeywordUnsigned ('unsigned')
%keyword KeywordVoid ('void')
%keyword KeywordVolatile ('volatile')
%keyword KeywordWhile ('while')
%keyword KeywordAlignas ('_Alignas')
%keyword KeywordAlignof ('_Alignof')
%keyword KeywordAtomic ('_Atomic')
%keyword KeywordAtomicWithLeftParen ('_Atomic(')
%keyword KeywordBool ('_Bool')
%keyword KeywordComplex ('_Complex')
%keyword KeywordGeneric ('_Generic')
%keyword KeywordImaginary ('_Imaginary')
%keyword KeywordNoreturn ('_Noreturn')
%keyword KeywordStaticAssert ('_Static_assert')
%keyword KeywordThreadLocal ('_Thread_local')
%keyword KeywordChar8T ('__char8_t')
%keyword KeywordChar16T ('__char16_t')
%keyword KeywordChar32T ('__char32_t')
%keyword KeywordWCharT ('__wchar_t')
%keyword KeywordIntPtrT ('__intptr_t')

%token Identifier ("identifier") : Identifier
%token IntegerConstant ("integer constant") : IntegerConstant
%token FloatingConstant ("floating constant") : FloatingConstant
%token CharacterConstant ("character constant") : CharacterConstant
%token StringLiteral ("string literal") : StringLiteral

%token LeftBracket ('[')
%token RightBracket (']')
%token LeftParen ('(')
%token RightParen (')')
%token LeftCurly ('{')
%token RightCurly ('}')
%token Dot ('.')
%token Arrow ('->')

%token Increment ('++')
%token Decrement ('--')
%token BitwiseAnd ('&')
%token Multiply ('*')
%token Add ('+')
%token Subtract ('-')
%token BitwiseNot ('~')
%token LogicalNot ('!')

%token Divide ('/')
%token Modulo ('%')
%token LeftShift ('<<')
%token RightShift ('>>')
%token LessThan ('<')
%token GreaterThan ('>')
%token LessEqual ('<=')
%token GreaterEqual ('>=')
%token EqualTo ('==')
%token NotEqualTo ('!=')
%token BitwiseXor ('^')
%token BitwiseOr ('|')
%token LogicalAnd ('&&')
%token LogicalOr ('||')

%token Conditional ('?')
%token Colon (':')
%token Semicolon (';')
%token Ellipsis ('...')

%token Assign ('=')
%token MultiplyAssign ('*=')
%token DivideAssign ('/=')
%token ModuloAssign ('%=')
%token AddAssign ('+=')
%token SubtractAssign ('-=')
%token LeftShiftAssign ('<<=')
%token RightShiftAssign ('>>=')
%token BitwiseAndAssign ('&=')
%token BitwiseXorAssign ('^=')
%token BitwiseOrAssign ('|=')

%token Comma (',')
%token Stringify ('#')
%token Concat ('##')

%token LeftBracketDigraph ('<:')
%token RightBracketDigraph (':>')
%token LeftCurlyDigraph ('<%')
%token RightCurlyDigraph ('%>')
%token StringifyDigraph ('%:')
%token ConcatDigraph ('%:%:')

%xstart IDENTIFIER
%xstart CHARACTER, CHARACTER1
%xstart STRING

%goal TranslationUnit
%goal Expression
%goal Declaration
%goal Statement

%shift 'else'

Identifier = (name : string)
IntegerConstant = (value : uint64, `type` : TypeName)
FloatingConstant = (value : float64, `type` : TypeName)

CharacterConstant:
    Char (value : char8)
    Char16 (value : char16)
    Char32 (value : char32)
    WChar (value : char16)
    attributes! (`type` : TypeName)

StringLiteral:
    Char (value : global::System.Collections.ObjectModel.ReadOnlyCollection[char8])
    Char8 (value : global::System.Collections.ObjectModel.ReadOnlyCollection[char8])
    Char16 (value : global::System.Collections.ObjectModel.ReadOnlyCollection[char16])
    Char32 (value : global::System.Collections.ObjectModel.ReadOnlyCollection[char32])
    WChar (value : global::System.Collections.ObjectModel.ReadOnlyCollection[char16])
    attributes! (`type` : TypeName)

ICharacterConstantFormatter.Format(n : CharacterConstant) -> string
IStringLiteralFormatter.Format(n : StringLiteral) -> string

ArgumentList = (items : [Expression])

Expression:
    NameReference (name : Identifier)
    IntegerConstant (value : IntegerConstant)
    FloatingConstant (value : FloatingConstant)
    CharacterConstant (value : CharacterConstant)
    StringLiteral (value : StringLiteral)

    Group (operand : Expression)

    Subscript (operand : Expression, index : Expression)
    Call (operand : Expression, arguments : ArgumentList)
    DotAccess (operand : Expression, member : Identifier)
    ArrowAccess (operand : Expression, member : Identifier)
    PostfixIncrement (operand : Expression)
    PostfixDecrement (operand : Expression)

    PrefixIncrement (operand : Expression)
    PrefixDecrement (operand : Expression)
    AddressOf (operand : Expression)
    Dereference (operand : Expression)
    Positive (operand : Expression)
    Negative (operand : Expression)
    BitwiseNot (operand : Expression)
    LogicalNot (operand : Expression)
    SizeofValue (operand : Expression)
    Sizeof (`type` : TypeName)
    Alignof (`type` : TypeName)
    Cast (operand : Expression, `type` : TypeName)

    Multiply (left : Expression, right : Expression)
    Divide (left : Expression, right : Expression)
    Modulo (left : Expression, right : Expression)
    Add (left : Expression, right : Expression)
    Subtract (left : Expression, right : Expression)

    LeftShift (left : Expression, right : Expression)
    RightShift (left : Expression, right : Expression)

    LessThan (left : Expression, right : Expression)
    GreaterThan (left : Expression, right : Expression)
    LessEqual (left : Expression, right : Expression)
    GreaterEqual (left : Expression, right : Expression)
    EqualTo (left : Expression, right : Expression)
    NotEqualTo (left : Expression, right : Expression)

    BitwiseAnd (left : Expression, right : Expression)
    BitwiseXor (left : Expression, right : Expression)
    BitwiseOr (left : Expression, right : Expression)

    LogicalAnd (left : Expression, right : Expression)
    LogicalOr (left : Expression, right : Expression)

    Conditional (condition : Expression, truePart : Expression, falsePart : Expression)

    Assign (left : Expression, right : Expression)
    MultiplyAssign (left : Expression, right : Expression)
    DivideAssign (left : Expression, right : Expression)
    ModuloAssign (left : Expression, right : Expression)
    AddAssign (left : Expression, right : Expression)
    SubtractAssign (left : Expression, right : Expression)
    LeftShiftAssign (left : Expression, right : Expression)
    RightShiftAssign (left : Expression, right : Expression)
    BitwiseAndAssign (left : Expression, right : Expression)
    BitwiseXorAssign (left : Expression, right : Expression)
    BitwiseOrAssign (left : Expression, right : Expression)

    Comma (left : Expression, right : Expression)

    Constant (operand : Expression)
    Argument (operand : Expression)

Declaration:
    Data (specifiers : DeclarationSpecifierList, declarators : InitDeclaratorList?)
    StaticAssertion (declaration : StaticAssertion)

DeclarationSpecifier:
    StorageClassSpecifier (specifier : StorageClassSpecifier)
    TypeSpecifier (specifier : TypeSpecifier)
    TypeQualifier (qualifier : TypeQualifier)
    FunctionSpecifier (specifier : FunctionSpecifier)
    AlignmentSpecifier (specifier : AlignmentSpecifier)

DeclarationSpecifierList = (items : [DeclarationSpecifier]+)

InitDeclarator = (declarator : Declarator, initializer : Initializer?)
InitDeclaratorList = (items : [InitDeclarator]+)

StorageClassSpecifier:
    Typedef
    Extern
    Static
    ThreadLocal
    Auto
    Register

TypeSpecifier:
    Void
    Char
    Short
    Int
    Long
    Char8T
    Char16T
    Char32T
    WCharT
    IntPtrT
    Float
    Double
    Signed
    Unsigned
    Bool
    Complex
    Atomic (typeName : TypeName)
    Struct (isUnion : bool, tagName : Identifier?, declarations : StructDeclarationList)
    PartialStruct (isUnion : bool, tagName : Identifier)
    Enum (tagName : Identifier?, enumerators : EnumeratorList, hasTrailingComma : bool)
    PartialEnum (tagName : Identifier)
    TypedefName (name : TypedefName)

StructDeclaration:
    FieldDeclaration (specifierQualifiers : SpecifierQualifierList, declarators : StructDeclaratorList?)
    StaticAssertion (declaration : StaticAssertion)

StructDeclarationList = (items : [StructDeclaration]+)

SpecifierQualifierListItem:
    TypeSpecifier (specifier : TypeSpecifier)
    TypeQualifier (qualifier : TypeQualifier)
    AlignmentSpecifier (specifier : AlignmentSpecifier)

SpecifierQualifierList = (items : [SpecifierQualifierListItem]+)

StructDeclarator:
    Field (declarator : Declarator)
    BitField (declarator : Declarator?, bits : Expression)

StructDeclaratorList = (items : [StructDeclarator]+)

Enumerator = (name : Identifier, value : Expression?)
EnumeratorList = (items : [Enumerator]+)

TypeQualifier:
    Const
    Restrict
    Volatile
    Atomic

FunctionSpecifier:
    Inline
    Noreturn

AlignmentSpecifier:
    Alignas (typeName : TypeName)
    AlignasValue (value : Expression)

Declarator = (pointer : PointerDeclarator?, direct : DirectDeclarator)

DirectDeclarator:
    Name (name : Identifier)
    Group (declarator : Declarator)
    Array (declarator : DirectDeclarator, qualifiers : TypeQualifierList?, size : Expression?)
    PrefixStaticArray (declarator : DirectDeclarator, qualifiers : TypeQualifierList?, size : Expression)
    PostfixStaticArray (declarator : DirectDeclarator, qualifiers : TypeQualifierList, size : Expression)
    StarArray (declarator : DirectDeclarator, qualifiers : TypeQualifierList?)
    Function (declarator : DirectDeclarator, parameters : ParameterDeclarationList)
    OldStyleFunction (declarator : DirectDeclarator, parameters : [Identifier])

PointerDeclarator = (items : [PointerDeclaratorItem]+)
PointerDeclaratorItem = (qualifiers : TypeQualifierList?)

TypeQualifierList = (items : [TypeQualifier]+)

ParameterDeclarationList = (items : [ParameterDeclaration]+, hasTrailingEllipsis : bool)

ParameterDeclaration:
    Concrete (declarator : Declarator)
    Abstract (declarator : AbstractDeclarator?)
    attributes (specifiers : DeclarationSpecifierList)

TypeName = (specifierQualifiers : SpecifierQualifierList, declarator : AbstractDeclarator?)

AbstractDeclarator:
    Pointer (declarator : PointerDeclarator)
    PointerDirect (declarator : PointerDeclarator?, direct : DirectAbstractDeclarator)

DirectAbstractDeclarator:
    Group (declarator : AbstractDeclarator)
    Array (declarator : DirectAbstractDeclarator?, qualifiers : TypeQualifierList?, size : Expression?)
    PrefixStaticArray (declarator : DirectAbstractDeclarator?, qualifiers : TypeQualifierList?, size : Expression)
    PostfixStaticArray (declarator : DirectAbstractDeclarator?, qualifiers : TypeQualifierList, size : Expression)
    StarArray (declarator : DirectAbstractDeclarator?)
    Function (declarator : DirectAbstractDeclarator?, parameters : ParameterDeclarationList)
    OldStyleFunction (declarator : DirectAbstractDeclarator?)

TypedefName = (name : Identifier)

Initializer:
    Single (value : Expression)
    Multiple (initializers : InitializerList, hasTrailingComma : bool)

InitializerListItem = (designation : Designation?, value : Initializer)
InitializerList = (items : [InitializerListItem]+)

Designation = (designators : DesignatorList)
DesignatorList = (items : [Designator]+)

Designator:
    Subscript (index : Expression)
    Field (name : Identifier)

StaticAssertion = (value : Expression, message : Expression)

Statement:
    Label (label : Identifier, operand : Statement)
    Case (value : Expression, operand : Statement)
    Default (operand : Statement)
    Compound (operands : BlockItemList?)
    Expression (value : Expression)
    Null
    If (test : Expression, body : Statement, orElse : Statement?)
    Switch (test : Expression, body : Statement)
    While (test : Expression, body : Statement)
    DoWhile (body : Statement, test : Expression)
    For (init : Expression?, test : Expression?, step : Expression?, body : Statement)
    ForBind (bind : Declaration, test : Expression?, step : Expression?, body : Statement)
    Goto (label : Identifier)
    Continue
    Break
    Return (value : Expression?)

BlockItem:
    Declaration (declaration : Declaration)
    Statement (statement : Statement)

BlockItemList = (items : [BlockItem]+)

FunctionDefinition = (
    specifiers : DeclarationSpecifierList,
    declarator : Declarator,
    declarations : DeclarationList?,
    body : Statement
)

DeclarationList = (items : [Declaration]+)

ExternalDeclaration:
    Function (function : FunctionDefinition)
    Data (data : Declaration)

ExternalDeclarationList = (items : [ExternalDeclaration]+)

TranslationUnit = (declarations : ExternalDeclarationList)

%%

StringLiteralExpression as Expression:
    StringLiteral => Expression.NewStringLiteral($1, @@)

PrimaryExpression as Expression:
    Identifier => Expression.NewNameReference($1, @@)
    IntegerConstant => Expression.NewIntegerConstant($1, @@)
    FloatingConstant => Expression.NewFloatingConstant($1, @@)
    CharacterConstant => Expression.NewCharacterConstant($1, @@)
    StringLiteralExpression => $1
    # TODO: StringLiteral concat
    '(' Expression ')' => Expression.NewGroup($2, @@)
    # TODO: generic-selection

PostfixExpression as Expression:
    PrimaryExpression => $1
    PostfixExpression '[' Expression ']' => Expression.NewSubscript($1, $3, @@)
    PostfixExpression '<:' Expression ':>' => Expression.NewSubscript($1, $3, @@)
    PostfixExpression '(' ArgumentList ')' => Expression.NewCall($1, $3, @@)
    PostfixExpression '.' Identifier => Expression.NewDotAccess($1, $3, @@)
    PostfixExpression '->' Identifier => Expression.NewArrowAccess($1, $3, @@)
    PostfixExpression '++' => Expression.NewPostfixIncrement($1, @@)
    PostfixExpression '--' => Expression.NewPostfixDecrement($1, @@)
    # TODO: initializer-list

UnaryExpression as Expression:
    PostfixExpression => $1
    '++' UnaryExpression => Expression.NewPrefixIncrement($2, @@)
    '--' UnaryExpression => Expression.NewPrefixDecrement($2, @@)
    '&' CastExpression => Expression.NewAddressOf($2, @@)
    '*' CastExpression => Expression.NewDereference($2, @@)
    '+' CastExpression => Expression.NewPositive($2, @@)
    '-' CastExpression => Expression.NewNegative($2, @@)
    '~' CastExpression => Expression.NewBitwiseNot($2, @@)
    '!' CastExpression => Expression.NewLogicalNot($2, @@)
    'sizeof' UnaryExpression => Expression.NewSizeofValue($2, @@)
    'sizeof' '(' TypeName ')' => Expression.NewSizeof($3, @@)
    '_Alignof' '(' TypeName ')' => Expression.NewAlignof($3, @@)

CastExpression as Expression:
    UnaryExpression => $1
    '(' TypeName ')' CastExpression => Expression.NewCast($4, $2, @@)

MultiplicativeExpression as Expression:
    CastExpression => $1
    MultiplicativeExpression '*' CastExpression => Expression.NewMultiply($1, $3, @@)
    MultiplicativeExpression '/' CastExpression => Expression.NewDivide($1, $3, @@)
    MultiplicativeExpression '%' CastExpression => Expression.NewModulo($1, $3, @@)

AdditiveExpression as Expression:
    MultiplicativeExpression => $1
    AdditiveExpression '+' MultiplicativeExpression => Expression.NewAdd($1, $3, @@)
    AdditiveExpression '-' MultiplicativeExpression => Expression.NewSubtract($1, $3, @@)

ShiftExpression as Expression:
    AdditiveExpression => $1
    ShiftExpression '<<' AdditiveExpression => Expression.NewLeftShift($1, $3, @@)
    ShiftExpression '>>' AdditiveExpression => Expression.NewRightShift($1, $3, @@)

RelationalExpression as Expression:
    ShiftExpression => $1
    RelationalExpression '<' ShiftExpression => Expression.NewLessThan($1, $3, @@)
    RelationalExpression '>' ShiftExpression => Expression.NewGreaterThan($1, $3, @@)
    RelationalExpression '<=' ShiftExpression => Expression.NewLessEqual($1, $3, @@)
    RelationalExpression '>=' ShiftExpression => Expression.NewGreaterEqual($1, $3, @@)

EqualityExpression as Expression:
    RelationalExpression => $1
    EqualityExpression '==' RelationalExpression => Expression.NewEqualTo($1, $3, @@)
    EqualityExpression '!=' RelationalExpression => Expression.NewNotEqualTo($1, $3, @@)

BitwiseAndExpression as Expression:
    EqualityExpression => $1
    BitwiseAndExpression '&' EqualityExpression => Expression.NewBitwiseAnd($1, $3, @@)

BitwiseXorExpression as Expression:
    BitwiseAndExpression => $1
    BitwiseXorExpression '^' BitwiseAndExpression => Expression.NewBitwiseXor($1, $3, @@)

BitwiseOrExpression as Expression:
    BitwiseXorExpression => $1
    BitwiseOrExpression '|' BitwiseXorExpression => Expression.NewBitwiseOr($1, $3, @@)

LogicalAndExpression as Expression:
    BitwiseOrExpression => $1
    LogicalAndExpression '&&' BitwiseOrExpression => Expression.NewLogicalAnd($1, $3, @@)

LogicalOrExpression as Expression:
    LogicalAndExpression => $1
    LogicalOrExpression '||' LogicalAndExpression => Expression.NewLogicalOr($1, $3, @@)

ConditionalExpression as Expression:
    LogicalOrExpression => $1
    LogicalOrExpression '?' Expression ':' ConditionalExpression => Expression.NewConditional($1, $3, $5, @@)

AssignmentExpression as Expression:
    ConditionalExpression => $1
    UnaryExpression '=' AssignmentExpression => Expression.NewAssign($1, $3, @@)
    UnaryExpression '*=' AssignmentExpression => Expression.NewMultiplyAssign($1, $3, @@)
    UnaryExpression '/=' AssignmentExpression => Expression.NewDivideAssign($1, $3, @@)
    UnaryExpression '%=' AssignmentExpression => Expression.NewModuloAssign($1, $3, @@)
    UnaryExpression '+=' AssignmentExpression => Expression.NewAddAssign($1, $3, @@)
    UnaryExpression '-=' AssignmentExpression => Expression.NewSubtractAssign($1, $3, @@)
    UnaryExpression '<<=' AssignmentExpression => Expression.NewLeftShiftAssign($1, $3, @@)
    UnaryExpression '>>=' AssignmentExpression => Expression.NewRightShiftAssign($1, $3, @@)
    UnaryExpression '&=' AssignmentExpression => Expression.NewBitwiseAndAssign($1, $3, @@)
    UnaryExpression '^=' AssignmentExpression => Expression.NewBitwiseXorAssign($1, $3, @@)
    UnaryExpression '|=' AssignmentExpression => Expression.NewBitwiseOrAssign($1, $3, @@)

CommaExpression as Expression:
    AssignmentExpression => $1
    CommaExpression ',' AssignmentExpression => Expression.NewComma($1, $3, @@)

Expression as Expression:
    CommaExpression => $1

ConstantExpression as Expression:
    ConditionalExpression => Expression.NewConstant($1, @@)

ArgumentExpression as Expression:
    AssignmentExpression => Expression.NewArgument($1, @@)

ArgumentList as ArgumentList:
    ArgumentExpression % ',' => ArgumentList.New($1, @@)

Declaration as Declaration:
    DeclarationSpecifierList InitDeclaratorList? ';' => Declaration.NewData($1, $2, @@)
    StaticAssertion => Declaration.NewStaticAssertion($1, @@)

DeclarationSpecifier as DeclarationSpecifier:
    StorageClassSpecifier => DeclarationSpecifier.NewStorageClassSpecifier($1, @@)
    TypeSpecifier => DeclarationSpecifier.NewTypeSpecifier($1, @@)
    TypeQualifier => DeclarationSpecifier.NewTypeQualifier($1, @@)
    FunctionSpecifier => DeclarationSpecifier.NewFunctionSpecifier($1, @@)
    AlignmentSpecifier => DeclarationSpecifier.NewAlignmentSpecifier($1, @@)

DeclarationSpecifierList as DeclarationSpecifierList:
    DeclarationSpecifier+ => DeclarationSpecifierList.New($1, @@)

InitDeclarator as InitDeclarator:
    Declarator => InitDeclarator.New($1, None, @@)
    Declarator '=' Initializer => InitDeclarator.New($1, $3, @@)

InitDeclaratorList as InitDeclaratorList:
    InitDeclarator %+ ',' => InitDeclaratorList.New($1, @@)

StorageClassSpecifier as StorageClassSpecifier:
    'typedef' => StorageClassSpecifier.NewTypedef(@@)
    'extern' => StorageClassSpecifier.NewExtern(@@)
    'static' => StorageClassSpecifier.NewStatic(@@)
    '_Thread_local' => StorageClassSpecifier.NewThreadLocal(@@)
    'auto' => StorageClassSpecifier.NewAuto(@@)
    'register' => StorageClassSpecifier.NewRegister(@@)

TypeSpecifier as TypeSpecifier:
    'void' => TypeSpecifier.NewVoid(@@)
    'char' => TypeSpecifier.NewChar(@@)
    'short' => TypeSpecifier.NewShort(@@)
    'int' => TypeSpecifier.NewInt(@@)
    'long' => TypeSpecifier.NewLong(@@)
    '__char8_t' => TypeSpecifier.NewChar8T(@@)
    '__char16_t' => TypeSpecifier.NewChar16T(@@)
    '__char32_t' => TypeSpecifier.NewChar32T(@@)
    '__wchar_t' => TypeSpecifier.NewWCharT(@@)
    '__intptr_t' => TypeSpecifier.NewIntPtrT(@@)
    'float' => TypeSpecifier.NewFloat(@@)
    'double' => TypeSpecifier.NewDouble(@@)
    'signed' => TypeSpecifier.NewSigned(@@)
    'unsigned' => TypeSpecifier.NewUnsigned(@@)
    '_Bool' => TypeSpecifier.NewBool(@@)
    '_Complex' => TypeSpecifier.NewComplex(@@)
    AtomicTypeSpecifier => $1
    StructOrUnionSpecifier => $1
    EnumSpecifier => $1
    # TODO: fix typedef name
    #TypedefName => TypeSpecifier.NewTypedefName($1, @@)

AtomicTypeSpecifier as AtomicTypeSpecifier:
    '_Atomic(' TypeName ')' => AtomicTypeSpecifier.New($2, @@)

TypeQualifier as TypeQualifier:
    'const' => TypeQualifier.NewConst(@@)
    'restrict' => TypeQualifier.NewRestrict(@@)
    'volatile' => TypeQualifier.NewVolatile(@@)
    '_Atomic' => TypeQualifier.NewAtomic(@@)

FunctionSpecifier as FunctionSpecifier:
    'inline' => FunctionSpecifier.NewInline(@@)
    '_Noreturn' => FunctionSpecifier.NewNoreturn(@@)

AlignmentSpecifier as AlignmentSpecifier:
    '_Alignas' '(' TypeName ')' => AlignmentSpecifier.NewAlignas($3, @@)
    '_Alignas' '(' ConstantExpression ')' => AlignmentSpecifier.NewAlignasValue($3, @@)

StructOrUnionSpecifier as TypeSpecifier:
    StructOrUnion Identifier? '{' StructDeclarationList '}' => TypeSpecifier.NewStruct($1, $2, $4, @@)
    StructOrUnion Identifier? '<%' StructDeclarationList '%>' => TypeSpecifier.NewStruct($1, $2, $4, @@)
    StructOrUnion Identifier => TypeSpecifier.NewPartialStruct($1, $2, @@)

StructOrUnion as bool:
    'struct' => False
    'union' => True

StructDeclaration as StructDeclaration:
    SpecifierQualifierList StructDeclaratorList? ';' => StructDeclaration.NewFieldDeclaration($1, $2, @@)
    StaticAssertion => StructDeclaration.NewStaticAssertion($1, @@)

StructDeclarationList as StructDeclarationList:
    StructDeclaration+ => StructDeclarationList.New($1, @@)

SpecifierQualifierListItem as SpecifierQualifierListItem:
    TypeSpecifier => SpecifierQualifierListItem.NewTypeSpecifier($1, @@)
    TypeQualifier => SpecifierQualifierListItem.NewTypeQualifier($1, @@)
    AlignmentSpecifier => SpecifierQualifierListItem.NewAlignmentSpecifier($1, @@)

SpecifierQualifierList as SpecifierQualifierList:
    SpecifierQualifierListItem+ => SpecifierQualifierList.New($1, @@)

StructDeclarator as StructDeclarator:
    Declarator => StructDeclarator.NewField($1, @@)
    Declarator? ':' ConstantExpression => StructDeclarator.NewBitField($1, $3, @@)

StructDeclaratorList as StructDeclaratorList:
    StructDeclarator %+ ',' => StructDeclaratorList.New($1, @@)

EnumSpecifier as TypeSpecifier:
    'enum' Identifier? '{' Enumerator %+ ',' '}' => TypeSpecifier.NewEnum($2, EnumeratorList.New($4, @4), False, @@)
    'enum' Identifier? '{' Enumerator %+ ',' ',' '}' => TypeSpecifier.NewEnum($2, EnumeratorList.New($4, @4), True, @@)
    'enum' Identifier? '<%' Enumerator %+ ',' '%>' => TypeSpecifier.NewEnum($2, EnumeratorList.New($4, @4), False, @@)
    'enum' Identifier? '<%' Enumerator %+ ',' ',' '%>' => TypeSpecifier.NewEnum($2, EnumeratorList.New($4, @4), True, @@)
    'enum' Identifier => TypeSpecifier.NewPartialEnum($2, @@)

Enumerator as Enumerator:
    Identifier => Enumerator.New($1, None, @@)
    Identifier '=' ConstantExpression => Enumerator.New($1, $3, @@)

Declarator as Declarator:
    PointerDeclarator? DirectDeclarator => Declarator.New($1, $2, @@)

DirectDeclarator as DirectDeclarator:
    Identifier => DirectDeclarator.NewName($1, @@)
    '(' Declarator ')' => DirectDeclarator.NewGroup($2, @@)

    DirectDeclarator '[' AssignmentExpression? ']' => DirectDeclarator.NewArray($1, None, $3, @@)
    DirectDeclarator '[' TypeQualifier+ AssignmentExpression? ']' => DirectDeclarator.NewArray($1, TypeQualifierList.New($3, @3), $4, @@)
    DirectDeclarator '[' 'static' AssignmentExpression ']' => DirectDeclarator.NewPrefixStaticArray($1, None, $4, @@)
    DirectDeclarator '[' 'static' TypeQualifier+ AssignmentExpression ']' => DirectDeclarator.NewPrefixStaticArray($1, TypeQualifierList.New($4, @4), $5, @@)
    DirectDeclarator '[' TypeQualifier+ 'static' AssignmentExpression ']' => DirectDeclarator.NewPostfixStaticArray($1, TypeQualifierList.New($3, @3), $5, @@)
    DirectDeclarator '[' '*' ']' => DirectDeclarator.NewStarArray($1, None, @@)
    DirectDeclarator '[' TypeQualifier+ '*' ']' => DirectDeclarator.NewStarArray($1, TypeQualifierList.New($3, @3), @@)
    DirectDeclarator '<:' AssignmentExpression? ':>' => DirectDeclarator.NewArray($1, None, $3, @@)
    DirectDeclarator '<:' TypeQualifier+ AssignmentExpression? ':>' => DirectDeclarator.NewArray($1, TypeQualifierList.New($3, @3), $4, @@)
    DirectDeclarator '<:' 'static' AssignmentExpression ':>' => DirectDeclarator.NewPrefixStaticArray($1, None, $4, @@)
    DirectDeclarator '<:' 'static' TypeQualifier+ AssignmentExpression ':>' => DirectDeclarator.NewPrefixStaticArray($1, TypeQualifierList.New($4, @4), $5, @@)
    DirectDeclarator '<:' TypeQualifier+ 'static' AssignmentExpression ':>' => DirectDeclarator.NewPostfixStaticArray($1, TypeQualifierList.New($3, @3), $5, @@)
    DirectDeclarator '<:' '*' ':>' => DirectDeclarator.NewStarArray($1, None, @@)
    DirectDeclarator '<:' TypeQualifier+ '*' ':>' => DirectDeclarator.NewStarArray($1, TypeQualifierList.New($3, @3), @@)

    DirectDeclarator '(' ParameterDeclaration %+ ',' ')' => DirectDeclarator.NewFunction($1, ParameterDeclarationList.New($3, False, @3), @@)
    DirectDeclarator '(' ParameterDeclaration %+ ',' ',' '...' ')' => DirectDeclarator.NewFunction($1, ParameterDeclarationList.New($3, True, @3.WithEnd(@5.End)), @@)
    DirectDeclarator '(' ')' => DirectDeclarator.NewOldStyleFunction($1, default([Identifier]), @@)
    DirectDeclarator '(' Identifier %+ ',' ')' => DirectDeclarator.NewOldStyleFunction($1, $3, @@)

PointerDeclarator as PointerDeclarator:
    PointerDeclaratorItem+ => PointerDeclarator.New($1, @@)

PointerDeclaratorItem as PointerDeclaratorItem:
    '*' => PointerDeclaratorItem.New(None, @@)
    '*' TypeQualifier+ => PointerDeclaratorItem.New(TypeQualifierList.New($2, @2), @@)

ParameterDeclaration as ParameterDeclaration:
    DeclarationSpecifierList Declarator => ParameterDeclaration.NewConcrete($1, $2, @@)
    DeclarationSpecifierList AbstractDeclarator => ParameterDeclaration.NewAbstract($1, $2, @@)
    DeclarationSpecifierList => ParameterDeclaration.NewAbstract($1, None, @@)

TypeName as TypeName:
    SpecifierQualifierList => TypeName.New($1, None, @@)
    SpecifierQualifierList AbstractDeclarator => TypeName.New($1, $2, @@)

AbstractDeclarator as AbstractDeclarator:
    PointerDeclarator => AbstractDeclarator.NewPointer($1, @@)
    PointerDeclarator? DirectAbstractDeclarator => AbstractDeclarator.NewPointerDirect($1, $2, @@)

DirectAbstractDeclarator as DirectAbstractDeclarator:
    '(' AbstractDeclarator ')' => DirectAbstractDeclarator.NewGroup($2, @@)

    '[' AssignmentExpression? ']' => DirectAbstractDeclarator.NewArray(None, None, $2, @@)
    '[' TypeQualifier+ AssignmentExpression? ']' => DirectAbstractDeclarator.NewArray(None, TypeQualifierList.New($2, @3), $3, @@)
    '[' 'static' AssignmentExpression ']' => DirectAbstractDeclarator.NewPrefixStaticArray(None, None, $3, @@)
    '[' 'static' TypeQualifier+ AssignmentExpression ']' => DirectAbstractDeclarator.NewPrefixStaticArray(None, TypeQualifierList.New($3, @4), $4, @@)
    '[' TypeQualifier+ 'static' AssignmentExpression ']' => DirectAbstractDeclarator.NewPostfixStaticArray(None, TypeQualifierList.New($2, @3), $4, @@)
    '[' '*' ']' => DirectAbstractDeclarator.NewStarArray(None, @@)
    '<:' AssignmentExpression? ':>' => DirectAbstractDeclarator.NewArray(None, None, $2, @@)
    '<:' TypeQualifier+ AssignmentExpression? ':>' => DirectAbstractDeclarator.NewArray(None, TypeQualifierList.New($2, @3), $3, @@)
    '<:' 'static' AssignmentExpression ':>' => DirectAbstractDeclarator.NewPrefixStaticArray(None, None, $3, @@)
    '<:' 'static' TypeQualifier+ AssignmentExpression ':>' => DirectAbstractDeclarator.NewPrefixStaticArray(None, TypeQualifierList.New($3, @4), $4, @@)
    '<:' TypeQualifier+ 'static' AssignmentExpression ':>' => DirectAbstractDeclarator.NewPostfixStaticArray(None, TypeQualifierList.New($2, @3), $4, @@)
    '<:' '*' ':>' => DirectAbstractDeclarator.NewStarArray(None, @@)

    '(' ParameterDeclaration %+ ',' ')' => DirectAbstractDeclarator.NewFunction(None, ParameterDeclarationList.New($2, False, @2), @@)
    '(' ParameterDeclaration %+ ',' ',' '...' ')' => DirectAbstractDeclarator.NewFunction(None, ParameterDeclarationList.New($2, True, @2.WithEnd(@4.End)), @@)
    '(' ')' => DirectAbstractDeclarator.NewOldStyleFunction(None, @@)

    DirectAbstractDeclarator '[' AssignmentExpression? ']' => DirectAbstractDeclarator.NewArray($1, None, $3, @@)
    DirectAbstractDeclarator '[' TypeQualifier+ AssignmentExpression? ']' => DirectAbstractDeclarator.NewArray($1, TypeQualifierList.New($3, @3), $4, @@)
    DirectAbstractDeclarator '[' 'static' AssignmentExpression ']' => DirectAbstractDeclarator.NewPrefixStaticArray($1, None, $4, @@)
    DirectAbstractDeclarator '[' 'static' TypeQualifier+ AssignmentExpression ']' => DirectAbstractDeclarator.NewPrefixStaticArray($1, TypeQualifierList.New($4, @4), $5, @@)
    DirectAbstractDeclarator '[' TypeQualifier+ 'static' AssignmentExpression ']' => DirectAbstractDeclarator.NewPostfixStaticArray($1, TypeQualifierList.New($3, @3), $5, @@)
    DirectAbstractDeclarator '[' '*' ']' => DirectAbstractDeclarator.NewStarArray($1, @@)
    DirectAbstractDeclarator '<:' AssignmentExpression? ':>' => DirectAbstractDeclarator.NewArray($1, None, $3, @@)
    DirectAbstractDeclarator '<:' TypeQualifier+ AssignmentExpression? ':>' => DirectAbstractDeclarator.NewArray($1, TypeQualifierList.New($3, @3), $4, @@)
    DirectAbstractDeclarator '<:' 'static' AssignmentExpression ':>' => DirectAbstractDeclarator.NewPrefixStaticArray($1, None, $4, @@)
    DirectAbstractDeclarator '<:' 'static' TypeQualifier+ AssignmentExpression ':>' => DirectAbstractDeclarator.NewPrefixStaticArray($1, TypeQualifierList.New($4, @4), $5, @@)
    DirectAbstractDeclarator '<:' TypeQualifier+ 'static' AssignmentExpression ':>' => DirectAbstractDeclarator.NewPostfixStaticArray($1, TypeQualifierList.New($3, @3), $5, @@)
    DirectAbstractDeclarator '<:' '*' ':>' => DirectAbstractDeclarator.NewStarArray($1, @@)

    DirectAbstractDeclarator '(' ParameterDeclaration %+ ',' ')' => DirectAbstractDeclarator.NewFunction($1, ParameterDeclarationList.New($3, False, @3), @@)
    DirectAbstractDeclarator '(' ParameterDeclaration %+ ',' ',' '...' ')' => DirectAbstractDeclarator.NewFunction($1, ParameterDeclarationList.New($3, True, @3.WithEnd(@5.End)), @@)
    DirectAbstractDeclarator '(' ')' => DirectAbstractDeclarator.NewOldStyleFunction($1, @@)

TypedefName as TypedefName:
    Identifier => TypedefName.New($1, @@)

Initializer as Initializer:
    AssignmentExpression => Initializer.NewSingle($1, @@)
    '{' InitializerListItem %+ ',' '}' => Initializer.NewMultiple(InitializerList.New($2, @2), False, @@)
    '{' InitializerListItem %+ ',' ',' '}' => Initializer.NewMultiple(InitializerList.New($2, @2), True, @@)
    '<%' InitializerListItem %+ ',' '%>' => Initializer.NewMultiple(InitializerList.New($2, @2), False, @@)
    '<%' InitializerListItem %+ ',' ',' '%>' => Initializer.NewMultiple(InitializerList.New($2, @2), True, @@)

InitializerListItem as InitializerListItem:
    Designation? Initializer => InitializerListItem.New($1, $2, @@)

Designation as Designation:
    DesignatorList '=' => Designation.New($1, @@)

DesignatorList as DesignatorList:
    Designator+ => DesignatorList.New($1, @@)

Designator as Designator:
    '[' ConstantExpression ']' => Designator.NewSubscript($2, @@)
    '<:' ConstantExpression ':>' => Designator.NewSubscript($2, @@)
    '.' Identifier => Designator.NewField($2, @@)

StaticAssertion as StaticAssertion:
    '_Static_assert' '(' ConstantExpression ',' StringLiteralExpression ')' ';' => StaticAssertion.New($3, $5, @@)

Statement as Statement:
    LabeledStatement => $1
    CompoundStatement => $1
    ExpressionStatement => $1
    NullStatement => $1
    SelectionStatement => $1
    IterationStatement => $1
    JumpStatement => $1

LabeledStatement as Statement:
    Identifier ':' Statement => Statement.NewLabel($1, $3, @@)
    'case' ConstantExpression ':' Statement => Statement.NewCase($2, $4, @@)
    'default' ':' Statement => Statement.NewDefault($3, @@)

CompoundStatement as Statement:
    '{' BlockItemList? '}' => Statement.NewCompound($2, @@)
    '<%' BlockItemList? '%>' => Statement.NewCompound($2, @@)

BlockItemList as BlockItemList:
    BlockItem+ => BlockItemList.New($1, @@)

BlockItem as BlockItem:
    Declaration => BlockItem.NewDeclaration($1, @@)
    Statement => BlockItem.NewStatement($1, @@)

ExpressionStatement as Statement:
    Expression ';' => Statement.NewExpression($1, @@)

NullStatement as Statement:
    ';' => Statement.NewNull(@@)

SelectionStatement as Statement:
    'if' '(' Expression ')' Statement => Statement.NewIf($3, $5, None, @@)
    'if' '(' Expression ')' Statement 'else' Statement => Statement.NewIf($3, $5, $7, @@)
    'switch' '(' Expression ')' Statement => Statement.NewSwitch($3, $5, @@)

IterationStatement as Statement:
    'while' '(' Expression ')' Statement => Statement.NewWhile($3, $5, @@)
    'do' Statement 'while' '(' Expression ')' ';' => Statement.NewDoWhile($2, $5, @@)
    'for' '(' Expression? ';' Expression? ';' Expression? ')' Statement => Statement.NewFor($3, $5, $7, $9, @@)
    'for' '(' Declaration Expression? ';' Expression? ')' Statement => Statement.NewForBind($3, $4, $6, $8, @@)

JumpStatement as Statement:
    'goto' Identifier ';' => Statement.NewGoto($2, @@)
    'continue' ';' => Statement.NewContinue(@@)
    'break' ';' => Statement.NewBreak(@@)
    'return' Expression? ';' => Statement.NewReturn($2, @@)

FunctionDefinition as FunctionDefinition:
    DeclarationSpecifierList Declarator DeclarationList? CompoundStatement => FunctionDefinition.New($1, $2, $3, $4, @@)

DeclarationList as DeclarationList:
    Declaration+ => DeclarationList.New($1, @@)

ExternalDeclaration as ExternalDeclaration:
    FunctionDefinition => ExternalDeclaration.NewFunction($1, @@)
    Declaration => ExternalDeclaration.NewData($1, @@)

ExternalDeclarationList as ExternalDeclarationList:
    ExternalDeclaration+ => ExternalDeclarationList.New($1, @@)

TranslationUnit as TranslationUnit:
    ExternalDeclarationList => TranslationUnit.New($1, @@)

%%

lower = ['a'-'z']
upper = ['A'-'Z']
digit = ['0'-'9']
nondigit = '_' | lower | upper

hex = ['0'-'9' 'A'-'F' 'a'-'f']
hex_quad = hex hex hex hex

<*>:
    nondigit =>: BeginString(IDENTIFIER); AppendTokenString()
    "\\u" hex_quad =>: BeginString(IDENTIFIER); AppendEscape(4, False)
    "\\U" hex_quad hex_quad =>: BeginString(IDENTIFIER); AppendEscape(8, False)

<IDENTIFIER>:
    nondigit =>: AppendTokenString()
    digit =>: AppendTokenString()
    "\\u" hex_quad =>: AppendEscape(4, False)
    "\\U" hex_quad hex_quad =>: AppendEscape(8, False)
    default =>: EndString(True); yield Identifier

nl = [ '\n' '\v' '\f' '\r' '\x85' '\u2028' '\u2029' ]
ws = [ '\t' ' ' '\xa0' '\u2000'-'\u200a' '\u202f' '\u205f' '\u3000' ]+
nnl = [^ '\n' '\v' '\f' '\r' '\x85' '\u2028' '\u2029' ]

<*>:
    nl =>: pass
    ws =>: pass

decimal_constant = ['1'-'9'] ['0'-'9']*
octal_constant = '0' ['0'-'7']*
hexadecimal_constant = '0' ['x' 'X'] hex+
integer_suffix = ( ['u' 'U'] ['l' 'L']?
                 | ['u' 'U'] ("ll" | "LL")
                 | ['l' 'L'] ['u' 'U']?
                 | ("ll" | "LL") ['u' 'U']? )

<*>:
    decimal_constant integer_suffix? =>: ScanIntegerValue(10); yield IntegerConstant
    octal_constant integer_suffix? =>: ScanIntegerValue(8); yield IntegerConstant
    hexadecimal_constant integer_suffix? =>: ScanIntegerValue(16); yield IntegerConstant

decimal_floating_constant = ( fractional_constant exponent_part? floating_suffix?
                            | ['0'-'9']+ exponent_part floating_suffix? )
hexadecimal_floating_constant = ( '0' ['x' 'X'] hexadecimal_fractional_constant binary_exponent_part floating_suffix?
                                | '0' ['x' 'X'] hex+ binary_exponent_part floating_suffix? )
fractional_constant = ['0'-'9']* '.' ['0'-'9']+ | ['0'-'9']+ '.'
exponent_part = ['e' 'E'] ['+' '-']? ['0'-'9']+
hexadecimal_fractional_constant = hex* '.' hex+ | hex+ '.'
binary_exponent_part = ['p' 'P'] ['+' '-']? ['0'-'9']+
floating_suffix = ['f' 'l' 'F' 'L']

<*>:
    decimal_floating_constant =>: ScanFloatingValue(10); yield FloatingConstant
    hexadecimal_floating_constant =>: ScanFloatingValue(16); yield FloatingConstant

<*>:
    ['L' 'u' 'U']? "'" =>: BeginString(CHARACTER)

<CHARACTER>:
    "'" =>: raise CharacterConstantEmpty()
    "\\'" =>: AppendChar('\''); GotoStart(CHARACTER1)
    "\\\"" =>: AppendChar('"'); GotoStart(CHARACTER1)
    "\\?" =>: AppendChar('?'); GotoStart(CHARACTER1)
    "\\\\" =>: AppendChar('\\'); GotoStart(CHARACTER1)
    "\\a" =>: AppendChar('\a'); GotoStart(CHARACTER1)
    "\\b" =>: AppendChar('\b'); GotoStart(CHARACTER1)
    "\\t" =>: AppendChar('\t'); GotoStart(CHARACTER1)
    "\\n" =>: AppendChar('\n'); GotoStart(CHARACTER1)
    "\\v" =>: AppendChar('\v'); GotoStart(CHARACTER1)
    "\\f" =>: AppendChar('\f'); GotoStart(CHARACTER1)
    "\\r" =>: AppendChar('\r'); GotoStart(CHARACTER1)
    "\\" ['0'-'7']{1,3} =>: AppendEscape(-1, True); GotoStart(CHARACTER1)
    "\\x" hex+ =>: AppendEscape(-1, False); GotoStart(CHARACTER1)
    "\\u" hex_quad =>: AppendEscape(4, False); GotoStart(CHARACTER1)
    "\\U" hex_quad hex_quad =>: AppendEscape(8, False); GotoStart(CHARACTER1)
    nnl =>: AppendTokenString(); GotoStart(CHARACTER1)
    default =>: raise UnterminatedCharacterConstant()

<CHARACTER1>:
    "'" =>: EndChar(); yield CharacterConstant
    nnl =>: raise CharacterConstantManyChars()
    default =>: raise UnterminatedCharacterConstant()

<*>:
    ("u8" | ['L' 'u' 'U'])? '"' =>: BeginString(STRING)

<STRING>:
    '"' =>: EndString(False); yield StringLiteral
    "\\'" =>: AppendChar('\'')
    "\\\"" =>: AppendChar('"')
    "\\?" =>: AppendChar('?')
    "\\\\" =>: AppendChar('\\')
    "\\a" =>: AppendChar('\a')
    "\\b" =>: AppendChar('\b')
    "\\t" =>: AppendChar('\t')
    "\\n" =>: AppendChar('\n')
    "\\v" =>: AppendChar('\v')
    "\\f" =>: AppendChar('\f')
    "\\r" =>: AppendChar('\r')
    "\\" ['0'-'7']{1,3} =>: AppendEscape(-1, True)
    "\\x" hex+ =>: AppendEscape(-1, False)
    "\\u" hex_quad =>: AppendEscape(4, False)
    "\\U" hex_quad hex_quad =>: AppendEscape(8, False)
    nnl =>: AppendTokenString()
    default =>: raise UnterminatedStringLiteral()
    